package com.ocm.core.service.impl;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.ocm.core.constant.OCMConstant;
import com.ocm.core.dto.GroupDto;
import com.ocm.core.dto.SecurityDto;
import com.ocm.core.dto.StorageDto;
import com.ocm.core.dto.UserInfoDto;
import com.ocm.core.exception.OCMRuntimeException;
import com.ocm.core.mapping.DTOConfiguration;
import com.ocm.core.mapping.IDMapping;
import com.ocm.core.model.ClassInstance;
import com.ocm.core.model.Files;
import com.ocm.core.service.ClassInstService;
import com.ocm.core.service.ContentEngineService;
import com.ocm.core.service.SecurityService;
import com.ocm.core.service.StorageService;
import com.opm.security.core.model.ExternalUserDetails;

@Service("localContentEngineService")
@Transactional(readOnly = true)
public class ContentEngineServiceImpl implements ContentEngineService {

   private ClassInstService classInstService;

   private StorageService storageService;

   private SecurityService securityService;

   @Autowired
   public ContentEngineServiceImpl(
         @Qualifier("storageService")StorageService storageService,
         @Qualifier("classInstService")ClassInstService classInstService,
         @Qualifier("securityService")SecurityService securityService) {
      this.storageService = storageService;
      this.classInstService = classInstService;
      this.securityService = securityService;
   }

   public StorageDto getStorage(String storageName) {
      StorageDto storageDto = this.storageService.getStorage(storageName, DTOConfiguration.instance(IDMapping.STORAGE_LIMIT));
      return storageDto;
   }

   public Files download(String fileGUID, String storageName, String[] properties) {
      String[] systemPropertyNames = new String[] {OCMConstant.DOCUMENT_TITLE, OCMConstant.CONTENT_TYPE, 
            OCMConstant.CONTENT_LENGTH, OCMConstant.FILE_NAME,OCMConstant.CONTENT_DATA};
      String[] propertyNames = null;
      if (properties.length > 0){
         propertyNames = Arrays.copyOf(systemPropertyNames, properties.length + systemPropertyNames.length);
         int index=systemPropertyNames.length;
         for (String propertyName: properties){
            propertyNames[index++] = propertyName;
         }
      }else{
         propertyNames = systemPropertyNames;
      }
      return this.classInstService.downloadFile(fileGUID, storageName, propertyNames);
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Exception.class, noRollbackFor= OCMRuntimeException.class)
   public String createInstance(String className, Map<String, Object> properties, StorageDto storage, List<SecurityDto> securityDtos) throws Exception {
      ClassInstance classInstance = this.classInstService.createInstance(storage.getId(), className, properties, securityDtos);
      return classInstance.getNodeUUID();
   }

   public Object fetchReferenceValues(String objectGUID, String propertyName, String[] propertyNames, int maxItems, StorageDto storage) {
      Object result = this.classInstService.getPropertyValue(objectGUID, propertyName, propertyNames, maxItems, storage);
      return result;
   }

   public Object downloadFromReferenceValues(String objectGUID, String propertyName, String[] properties, int maxItems, StorageDto storage) {
      String[] systemPropertyNames = new String[] {OCMConstant.DOCUMENT_TITLE, OCMConstant.CONTENT_TYPE, 
            OCMConstant.CONTENT_LENGTH, OCMConstant.FILE_NAME,OCMConstant.CONTENT_DATA};
      String[] propertyNames = null;
      if (properties.length > 0){
         propertyNames = Arrays.copyOf(systemPropertyNames, properties.length + systemPropertyNames.length);
         int index=systemPropertyNames.length;
         for (String includePropertyName: properties){
            propertyNames[index++] = includePropertyName;
         }
      }else{
         propertyNames = systemPropertyNames;
      }
      Object result = this.classInstService.getPropertyValue(objectGUID, propertyName, propertyNames, maxItems, storage);
      return result;
   }

   public UserInfoDto getContentCreator(StorageDto storage, String guid) {
      String createdUser = (String) this.classInstService.getPropertyValue(guid, "createdUser", null, -1, storage);
      UserInfoDto userInfoDto = (UserInfoDto) this.securityService.loadUserByUsername(createdUser);
      return userInfoDto;
   }

   public void delete(StorageDto storage, String guid){
      this.classInstService.deleteClassInstance(storage.getId(), guid);
   }

   public void updateInstance(String instancesUUID, Map<String, Object> properties, StorageDto storage) {
      this.classInstService.updateInstance(storage.getId(), instancesUUID, properties);
   }

   public void checkOut(String objectGUID, StorageDto storage) {
      this.classInstService.checkout(storage.getId(), objectGUID);
   }

   public void checkin(String objectGUID, Map<String, Object> properties, StorageDto storage) {
      this.classInstService.checkin(storage.getId(), objectGUID, properties);
   }
   
   public boolean hasExist(String objectGUID, StorageDto storage){
      return this.classInstService.hasExist(storage.getId(), objectGUID);
   }

   @Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Exception.class, noRollbackFor= OCMRuntimeException.class)
   public Long addNewUser(String username, String password, String[] defaultGroups) {
      UserInfoDto userInfoDto = new UserInfoDto();
      userInfoDto.setIsAccountNonExpired(true);
      userInfoDto.setIsAccountNonLocked(true);
      userInfoDto.setIsEnabled(false);
      userInfoDto.setIsUserSystem(false);
      userInfoDto.setUsername(username);
      userInfoDto.setPassword(password);
      List<GroupDto> groupDtos = this.securityService.findByGroupnames(defaultGroups);
      userInfoDto.setUserDetails(groupDtos);
      Long userId = this.securityService.addUser(userInfoDto);
      return userId;
   }

   public ExternalUserDetails loadByUsername(String username) {
      ExternalUserDetails userDetails = (ExternalUserDetails) this.securityService.loadUserByUsername(username);
      return userDetails;
   }

   public GroupDto loadByGroupname(String groupname) {
      GroupDto groupDto = this.securityService.findByGroupname(groupname);
      return groupDto;
   }
}
